package com.irdstudio.efp.esb.api.rest;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.irdstudio.basic.framework.core.constant.DateFormatConstant;
import com.irdstudio.basic.framework.core.util.DateTool;
import com.irdstudio.basic.framework.core.util.SpringContextUtils;
import com.irdstudio.basic.framework.core.util.StringUtil;
import com.irdstudio.basic.framework.core.util.TraceUtil;
import com.irdstudio.basic.framework.redis.redisutil.RedisUtil;
import com.irdstudio.efp.console.service.facade.PrdInfoService;
import com.irdstudio.efp.console.service.vo.PrdInfoVO;
import com.irdstudio.efp.esb.api.bean.nls.ReqLoanAppBean;
import com.irdstudio.efp.esb.api.bean.sx.ReqCreditBean;
import com.irdstudio.efp.esb.api.common.EsbRespServiceBeanCreator;
import com.irdstudio.efp.esb.api.util.TimeCompareUtil;
import com.irdstudio.efp.esb.common.constant.ESBServerConstance;
import com.irdstudio.efp.esb.common.constant.EsbBizEnums;
import com.irdstudio.efp.esb.common.constant.EsbSysHeadEnums;
import com.irdstudio.efp.esb.common.server.req.EsbReqServiceBean;
import com.irdstudio.efp.esb.common.server.req.EsbReqSysHeadBean;
import com.irdstudio.efp.esb.common.server.resp.EsbRespServiceBean;
import com.irdstudio.efp.nls.service.facade.NlsQueueSoltComnService;
import org.apache.logging.log4j.ThreadContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

@Controller
@RequestMapping("/esbBusiness")
public class EsbBusinessController {
	private static Logger logger = LoggerFactory.getLogger(EsbBusinessController.class);


	@Autowired
	@Qualifier("prdInfoService")
	private PrdInfoService prdInfoService;

	//需要做进件限制的服务码和场景码
	@Value("${access.limit.code}")
	private String limitCodes;
	/**
	 * redis
	 */
	@Autowired
	RedisUtil redis;


	private static final String key = "accessTimeLimit";

	/**
	 * ESB服务接口入口，该接口提供给开放平台调用，业务逻辑如下：<br>
	 * 1.将receiveMap转换成ESB请求报文对象，从该对象中获取请求系统头、请求应用头对象和请求BODY对象；<br>
	 * 2.从请求系统头对象中获取服务代码和场景，根据服务代码和场景来反射对应的处理类；<br>
	 * 3.调用方法将请求BODY对象传给对应的处理类，返回结果是字符串类型；<br>
	 * 4.根据返回结果分别组装响应系统头对象、响应应用头对象；<br>
	 * 5.组装响应报文对象；<br>
	 * v1.1: <br>
	 * 1.将请求报文转换成ESB请求体，获取交易码<br>
	 * 2.将请求报文转换成ESB请求体<br>
	 * 3.设置TraceId全局链路跟踪<br>
	 * 4.调用相关的ESB服务<br>
	 * 
	 * @param esbReqService,request
	 * @return
	 * @author wangyk3
	 * @author hengyh
	 * @author huangwb
	 * @since 2018年12月17日 下午6:55:50
	 * @version 1.1
	 * @throws Exception
	 */
	@PostMapping(value = "/doRecv")
	public String doRecv(@RequestBody Object esbReqService, HttpServletRequest request) throws Exception {
		ThreadContext.put(TraceUtil.ROUTINGKEY, "special");
		logger.info("请求json报文:" + JSON.toJSONString(esbReqService));
		EsbReqServiceBean esbReqServiceBean = null;// ESB请求体
		EsbReqSysHeadBean esbReqSysHeadBean = null;// ESB请求系统头

		// 1.将请求报文转换成ESB请求体，获取交易码
		esbReqServiceBean = JSONObject.parseObject(JSON.toJSONString(esbReqService), EsbReqServiceBean.class);
		request.setAttribute("esbReqServiceBean", esbReqServiceBean);
		esbReqSysHeadBean = esbReqServiceBean.getSysHead();
		// 服务代码
		String svcCd = esbReqSysHeadBean.getSvcCd();
		// 场景
		String scnCd = esbReqSysHeadBean.getScnCd();
		String tradeCode = ESBServerConstance.SERVICE_KEY_HEAD + svcCd + "_" + scnCd;// 交易码

		// 2.将请求报文转换成ESB请求体
		esbReqServiceBean = JSONObject.parseObject(JSON.toJSONString(esbReqService), EsbReqServiceBean.class);
 		String esbReqBody = JSON.toJSONString(esbReqServiceBean.getBODY());
		request.setAttribute("esbReqBody", esbReqBody);

		//进件时间控制,如果不通过则拒绝进件
		boolean isPass = accessTimeLimit(svcCd, scnCd, esbReqServiceBean);
		if(!isPass){
			return "forward:/esbBusiness/refuse";
		}

		// 3.设置TraceId全局链路跟踪
		String traceId = esbReqSysHeadBean.getGlblSrlNo();
		ThreadContext.put(TraceUtil.TRACEID, traceId);
		TraceUtil.setTraceId(traceId);
		String GlblSrlNo = esbReqSysHeadBean.getGlblSrlNo(); // 对应请求头全局流水号
		ThreadContext.put(TraceUtil.GLBLSRLNO, GlblSrlNo);
		TraceUtil.setGlblSrlNo(GlblSrlNo);
		String InrSrlNo = esbReqSysHeadBean.getCnsmrSrlNo(); // 对应请求头中的消费方流水号
		ThreadContext.put(TraceUtil.INRSRLNO, InrSrlNo);
		TraceUtil.setInrSrlNo(InrSrlNo);
		String SubtrxNo = ""; // 请求头中没有对应的字段
		ThreadContext.put(TraceUtil.SUBTRXNO, SubtrxNo);
		TraceUtil.setSubtrxNo(SubtrxNo);
		String CnsmrSysID = esbReqSysHeadBean.getCnsmrSysID(); // 对应请求头中的消费方系统编号
		ThreadContext.put(TraceUtil.CNSMRSYSID, CnsmrSysID);
		TraceUtil.setCnsmrSysID(CnsmrSysID);
		String SvcId = esbReqSysHeadBean.getSvcCd(); // 对应请求头中的服务代码
		ThreadContext.put(TraceUtil.SVCID, SvcId);
		TraceUtil.setSvcId(SvcId);
		String CnsmrTxnCd = tradeCode; // 交易码
		ThreadContext.put(TraceUtil.CNSMRTXNCD, CnsmrTxnCd);
		TraceUtil.setCnsmrTxnCd(CnsmrTxnCd);

		// 4.调用相关的ESB服务
		return "forward:/esb/" + tradeCode;
	}

	/**
	 * 进件时间控制：如果符合设定的场景码和服务吗，且受理开关打开，则要判断进件时间是否在限制的时间内，如果不在则返回合作方 非工作时间申请
	 * 有异常的情况下会告警，但是不影响接下来的流程，会继续走之后的流程，就算是被限制了的接口,在规定时间外进件，如果有异常也会继续走，不影响他接下来的流程
	 *   limitCodes格式：30220001-01,30220001-03
	 * @param svcCd 服务码
	 * @param scnCd 场景码
	 * @param esbReqServiceBean ESB请求体
	 * @return
	 * @author dengqiwen
	 * @since 2020年06月30日
	 */
	private boolean accessTimeLimit(String svcCd,String scnCd,EsbReqServiceBean esbReqServiceBean){
		boolean isPass=true;
		try {
			//limitCodes（需要做进件限制的服务码和场景码）为空就跳过，不影响其他流程
			if(!StringUtil.isNullorBank(limitCodes)){
				String[] limitCode = limitCodes.split(",");
				for (int i = 0; i < limitCode.length; i++) {
					String code = limitCode[i];
					String[] split = code.split("-");
					//先判断服务码
					if(svcCd.equals(split[0])){
						//场景码
						String limitScnCd = split[1];
						//再判断场景码,场景码符合就判断受理开关是否打开
						if(scnCd.equals(limitScnCd)){
							//拿到产品id
							//先从报文头中产品id，如果报文头中是空，就从报文体中拿，报文体中有2种产品id字段,PdCd和ProdCd
							EsbReqSysHeadBean esbReqSysHeadBean = esbReqServiceBean.getSysHead();

							//报文头的产品id
							String prdCode;
							if (StringUtil.isStrNotEmpty(esbReqSysHeadBean.getPrdctID())){
								prdCode=esbReqSysHeadBean.getPrdctID();
							}else{
								//请求体
								String esbReqBody = JSON.toJSONString(esbReqServiceBean.getBODY());
								JSONObject jsonObject = JSONObject.parseObject(esbReqBody);
								prdCode = (String) Optional.ofNullable(jsonObject.get("ProdCd")).orElse("");
								//百度、马上的产品id字段
								if (StringUtil.isStrEmpty(prdCode)){
									//优e贷、普税贷的产品id字段
									prdCode = (String) Optional.ofNullable(jsonObject.get("PdCd")).orElse("");
										//如果还是空就抛异常
										if (StringUtil.isStrEmpty(prdCode)){
											throw new Exception("进件时间控制产品id为空，请排查配置文件或报文");
										}
								}
							}
							// 根据产品代码查询产品id
							PrdInfoVO vo = new PrdInfoVO();
							vo.setPrdCode(prdCode);
							PrdInfoVO outputVo = prdInfoService.queryLastPrdInfo(vo);
							//受理开关打开就继续
							if(EsbBizEnums.LimitSwitch.OPEN.getValue().equals(outputVo.getAcceptStatus())){
								//判断进件时间（机器时间）是否在要求的时间内
								//限制的开始时间
								String startTime = outputVo.getAcceptStartTime();
								//限制的结束时间
								String endTime = outputVo.getAcceptEndTime();
								//当前机器时间
								String applyTime = DateTool.formateDate(new Date(), DateFormatConstant.TIME_FORMAT);
								isPass = TimeCompareUtil.compareTime(startTime, applyTime, endTime);
							}
						}
					}
				}
			}

		} catch (Exception e) {
			e.printStackTrace();
			logger.error("进件时间控制出现异常,异常信息："+e.getMessage());
			//出现异常则告警，告警有效期1小时，1小时之后还有问题继续告警
                try {
                    if (redis.setIfAbsent(key, "")) {
						redis.expire(key, 1, TimeUnit.HOURS);
						NlsQueueSoltComnService nlsQueueSoltComnService = (NlsQueueSoltComnService) SpringContextUtils.getBean("nlsQueueSoltComnService");
                        nlsQueueSoltComnService.alarmNotice(EsbBizEnums.AlarmObjectName.OCM_REAL_TIME.getValue(),
                                EsbBizEnums.AlarmLvl.SERIOUS.getValue(), "进件时间控制出现异常,异常信息："+e.getMessage());
                    }else{
						logger.error("redis一小时有效期未到，不告警！");
					}
                } catch (Exception e1) {
                    e1.printStackTrace();
                    logger.error("进件时间控制-告警接口出现异常,异常信息："+e1.getMessage());
                }
        }
		return isPass;
	}

	/**
	 * 拒绝进件返回信息
	 *
	 * @param request
	 * @return
	 */
	@ResponseBody
	@PostMapping("/refuse")
	public EsbRespServiceBean refuseCreditProcess(HttpServletRequest request) {
		String esbReqBody = (String) request.getAttribute("esbReqBody");
		EsbReqServiceBean esbReqServiceBean = (EsbReqServiceBean) request.getAttribute("esbReqServiceBean");
		ReqCreditBean reqCreditBean = JSONObject.parseObject(esbReqBody, ReqCreditBean.class);
		String applSeq = reqCreditBean.getCrdtAppFlowNo();
		return EsbRespServiceBeanCreator.buildesbRespServiceBean(esbReqServiceBean,
				// 为兼容各种场景，返回体送空字符串
				"{}",
				EsbSysHeadEnums.RetStEnum.FAILURE.getCode(),
				EsbBizEnums.RetCodeEnum.FGZSJSQ.getVALUE(),
				"非工作时间申请");
	}


}
